KNOWLEDGE DATABASE : HARD CODED CONTENT : To the "packages/hud" to ! QUESTION ABOUT THE BEST TO DO : abs = [ * (? (< $arg1) -1 1) $arg1 ] absf = [ sqrt (pow $arg1 2) ] I do that : abs = [ if (< $arg1 1) [ result (*f $arg1 -1) ] [ result $arg1 ] ] ??? I dont understand what is the "alternative shaders" ! ??? Bug (or how that work) when a "normalmap" is missing !??? ABOUT TEXTURE : # Please do me a favour and remove all "0 0 0 .125", as they are useless at any other texture as the diffuse map (at least for Sauerbraten, not sure for Red Eclipse). That's why I would recommend to use "texscale .125" instead, as it's way easier for new beings to understand and avoids a lot of clutter and would also be compatible with Red Eclipse if you do are able to scale any other texture as the diffuse map. My first question is : How work this fu**ing secondary texture ??? I made tests and : nothing. It's always the first which is displayed !!! # DECALWORLD setshader decalworld texture 0 "dg/floor_grass3&soil.jpg" texture d "textures/frenchbadping/footprint.png" The footprint MUST have an alpha-channel to work, else black (or white) pixels will be displayed instead of the pixels which should contain an alpha-channel. It won't have any bump-, specular-, height-mapped texture, neither environmental-mapped rendering. This is just because Sauerbraten isn't allowing to combine decals with anything else than the diffuse texture. I hope this will be changed in the future... # PULSEGLOW setshader bumpspecmapparallaxpulseglowworld setshaderparam glowcolor .5 .5 .5 setshaderparam pulseglowspeed .5 setshaderparam pulseglowcolour .9 .9 .9 # Red Eclipse uses the same set up for textures as Sauerbraten does. It even uses the same stdshader.cfg (as I noticed). # If you want to import a map from Sauerbraten to Red Eclipse, just rename the ".ogz" format into ".mpz" (I'm not sure but it's recommended to try this on a map of the trooper- or justice-edition). # DEFAULT SHADER VALUES : I think that all default values are in the "data/stdshader.cfg" file ! I found (but without tested) this : - For the shaderparam "glowcolor" : 1 ; 1 ; 1 - For the shaderparam "pulsespeed" : 1 (Hz) - For the setshader "pulseglow" : * for the shaderparam "glowcolor" : 1 1 1 * for the shaderparam "pulseglowcolor" : 0 0 0 * for the shaderparam "pulseglowspeed" : 1 (Hz) - For the shaderparam "parallaxscale" : 0.06 -0.03 (ho ho :-))) - For the specularity : * If no specmap is done : shaderparam "specscale" 1 1 1 * If specmap is present : shaderparam "specscale" 6 6 6 - For the envmap (reflectivity) : * If no envmap is done : shaderparam "envscale" 0.2 0.2 0.2 * If envmap is present : shaderparam "envscale" 1 1 1 STARTUP ORDER SEQUENTIALLY : if the game is launched with the argument -r, it executes init.cfg (or anotherscript defined if arg is "-rscript.cfg" where script.cfg is the script) init.cfg "packages/textures/notexture.png" data/stdlib.cfg data/font.cfg data/stdshader.cfg (or data/glsl.cfg if set for glsl) data/keymap.cfg data/stdedit.cfg data/menus.cfg data/sounds.cfg data/brush.cfg mybrushes.cfg (if it exists) servers.cfg (if it exists) config.cfg (if it exists, otherwise data/defaults.cfg) autoexec.cfg data/game_fps.cfg (or "data/game_rpg.cfg") auth.cfg (if it exists) once.cfg (if it exists, and is then deleted) "-xscript" is executed if it is included in the arguments (this is not a file, but actual cubescript, so "script" is executed here) EDIT: -r is usually included in the .bat file & bash scripts, etc Do remember that init.cfg is where all the video and audio settings are and really shouldn't be messed with. Also, you're welcome! :) ABOUT SCRIPTING : ------------------------------- "Indeed, I discovered that the variable arguments ($ argX)" Here are two main concepts of cubescript: 1) EVERYTHING is global. 2) EVERYTHING is a string. ------------------------------- # ABOUT @ $ [] () Most of what we know, we learned ourselves. Only the most basic stuff lies in the documentation, and the few advanced functions explained in there are disregarded by newbies anyway. Regarding the '@', think of square brackets as levels. Example: [level_1 [level_2 [level_3]]] The '@' can be used to retrieve the value of a variable from a previous level. Most of the time it's not necessary to use it, since a variable's value remains consistent throughout, but for the few cases it doesn't, '@' is necessary. Take for example a sleeping loop. [loop i 10 [sleep (* $i 200) [say @i]]] The loop itself is the first level. The sleep is the second level and 'say' is the third level. Due to the sleep, the value of 'i' is inconsistent throughout the whole example. The value of 'i' in the first level is likely a stable integer. The loop changes that, so the value of 'i' on the second level is define by the loop itself. However, in the third level, 'i' is once more stable, and so using $i wouldn't reflect the value the loop is forcing 'i' to be, and that's where '@' comes to use. However, '@' is a bit quirky and may cause problems in a script if used where it is not really needed. 'if' operators should for no reason require variables of subsequent levels to use '@' unless a loop or sleep, or any other command that may offset things takes place. I call value inconsistency "offset", just to be clear. :P Square brackets are meant for executing commands, while round brackets practically process their content before the rest of the commands take place. Do not be mistaken however, square brackets are also containers, and may be used to act as a list, whether for an array of strings or a single value, it's all valid. A few examples: clampval = [max $arg2 (min $arg3 $arg1)] To use it, you'd have to do (clampval VARIABLE MIN MAX). if you did $clampval you'd just get a result of the contents, which isn't what you want. Another sample function for the round brackets now : client_auth = (? (= (&& (isconnected) (hasauthkey))) "^f4") To use it, you'd simply do $client_auth, since it's MADE to process the given data before hand, so you just grab the value it "contains". This does not imply that the function aliases itself to a value. It merely acts as a "relay" to the code it is aliased to. ------------------------------- # "writecfg" command which save the settings in the "config.cfg" and in the the cfg file if it given. # ' = ' and '(alias )' both do the same thing: # '$' and '(getalias )' both do the same thing: # Note: arg1 is the global variable, not $arg1. One thing you need to learn/figure out is the process in which the parser executes the script. # You can always use ["%1"] instead of "^"%1^"". Easier to read and use. "[%1]" will not work however. =P # command = [result (+ 3 2)] is the same as command = [+ 3 2] # ?: ? cond A B -- If cond is true, return A; otherwise return B (e.g. (? (= 1 1) "yes" "no") would return "yes") # if: if cond true false -- Executes A if cond is true, otherwise it executes B (e.g. /if (= 3 5) [result "what?"] [result "nope"]) # (=s A B) is equivalent to (strcmp A B) # (indexof [list] [element]) returns the index of the given element in the given list, and -1 if the element does not appear in the list. # 'createine' (i.e., "create if not existing") defines a variable if it is not defined, so you can set default values for various variables. # The "applydialog" var just turns on & off the apply settings dialog for things like changing the resolution. # tabify before_text number_of_tabulators #listfind i $list [ body where "i" is the content of each index cycled through ] ---------------- # case/casef/cases are a switch-like command for ints/floats/strings respectively. The proper structure: case [ ...] [] Notes: [] means arguments are optional. ... means the grouped arguments can be repeated ad infinitum (although there is a 24 argument limit in Sauerbraten) value - the type is dependent on which command is used (case/f/s) e.g. case $test 1 [ echo found 1! ] 5 [ echo found 5! ] [ echo nothing found! ] output for: test=1: "found 1!" test=5: "found 5!" test=3: "nothing found" ---------------- # APPEND : myaddfunc = [ result (concat $arg1 (format "^"%1^"" $arg2) ) ] list = myaddfunc $list $content_with_spaces I'll modify your version a bit to show you what's different from my version of 'append': myaddfunc = [$arg1 = (concat (getalias $arg1) (format ["%1"] $arg2))] ^^^ - Allows you to add to given list a string containing spaces. Only way to add multiple ones at a time though is through /myaddfunc LIST ["this is a long string" "and again a long string"] append = [$arg1 = (concat (getalias $arg1) $arg2)] ^^^ - Allows you to add to given list a string without spaces, or treat a string/list as multiple items to add. ------------------------------- # SLEEP AND LOOP/WHILE The reason your code crashes is because there is only 1 script thread, everything is sequential and every stored sleep body must wait for all previously scheduled scripts to execute. So in your while loop, those sleeps will not execute until the while is finished (which is never, since i doesn't change in the body outside of the sleeps) The reason sleep 10 (...) doesn't work is because of the @, the @ will be taken at the time of parsing the while loop and it's arguments @ it will always be 1. So therefore, the body of the while loop would be: [ sleep 10 (i = (+ 1 1); echo 1) ] at all times, and the while loop will never end because i will always be 2 (< 10). Just do what you did in myfunc (with the @): i = 0; while [ < $i 10 ] [ sleep 1000 [ echo @i ]; i = (+ $i 1) ] Although if you only want to loop from 0->10 use: loop i 10 [ sleep 1000 [ echo @i ] ] Since it will automatically increment i. The count var (here "i"), is temporary ! ---------------------------------------------------------------- IN THE SOURCE CODE : IN THE "SRC/ENGINE/COMMAND.CPP" : void writeescapedstring(stream *f, const char *s) { f->putchar('"'); for(; *s; s++) switch(*s) { case '\n': f->write("^n", 2); break; // Break line case '\t': f->write("^t", 2); break; // Tabulator case '\f': f->write("^f", 2); break; // Colors 0-7 (green, blue, yellow, red, grey, magenta, orange, white) case '"': f->write("^\"", 2); break; // "\" ??? ^\" give "\ " (with a space !) default: f->putchar(*s); break; // By default, with others signs, the "^" is omitted. // ^" for the sign " (usefull !) } f->putchar('"'); } COMMAND(push, "ss"); COMMAND(pop, "s"); COMMAND(resetvar, "s"); COMMAND(alias, "ss"); COMMAND(writecfg, "s"); ICOMMAND(if, "sss", (char *cond, char *t, char *f), commandret = executeret(cond[0] && (!isinteger(cond) || parseint(cond)) ? t : f)); ICOMMAND(?, "sss", (char *cond, char *t, char *f), result(cond[0] && (!isinteger(cond) || parseint(cond)) ? t : f)); ICOMMAND(loop, "sis", (char *var, int *n, char *body), ICOMMAND(loopwhile, "siss", (char *var, int *n, char *cond, char *body), ICOMMAND(while, "ss", (char *cond, char *body), while(execute(cond)) execute(body)); // can't get any simpler than this :) ICOMMAND(exec, "s", (char *file), execfile(file)); COMMAND(concat, "C"); COMMAND(result, "s"); COMMAND(concatword, "V"); COMMAND(format, "V"); COMMAND(at, "si"); COMMAND(substr, "sis"); ICOMMAND(listlen, "s", (char *s), intret(listlen(s))); COMMANDN(getalias, getalias_, "s"); COMMAND(prettylist, "ss"); ICOMMAND(listdel, "ss", (char *list, char *del), commandret = listdel(list, del)); ICOMMAND(indexof, "ss", (char *list, char *elem), intret(listincludes(list, elem, strlen(elem)))); ICOMMAND(listfind, "sss", (char *var, char *list, char *body), looplist(var, list, body, true)); ICOMMAND(looplist, "sss", (char *var, char *list, char *body), looplist(var, list, body, false)); ICOMMAND(loopfiles, "ssss", (char *var, char *dir, char *ext, char *body), ICOMMAND(+, "ii", (int *a, int *b), intret(*a + *b)); ICOMMAND(*, "ii", (int *a, int *b), intret(*a * *b)); ICOMMAND(-, "ii", (int *a, int *b), intret(*a - *b)); ICOMMAND(+f, "ff", (float *a, float *b), floatret(*a + *b)); ICOMMAND(*f, "ff", (float *a, float *b), floatret(*a * *b)); ICOMMAND(-f, "ff", (float *a, float *b), floatret(*a - *b)); ICOMMAND(=, "ii", (int *a, int *b), intret((int)(*a == *b))); ICOMMAND(!=, "ii", (int *a, int *b), intret((int)(*a != *b))); ICOMMAND(<, "ii", (int *a, int *b), intret((int)(*a < *b))); ICOMMAND(>, "ii", (int *a, int *b), intret((int)(*a > *b))); ICOMMAND(<=, "ii", (int *a, int *b), intret((int)(*a <= *b))); ICOMMAND(>=, "ii", (int *a, int *b), intret((int)(*a >= *b))); ICOMMAND(=f, "ff", (float *a, float *b), intret((int)(*a == *b))); ICOMMAND(!=f, "ff", (float *a, float *b), intret((int)(*a != *b))); ICOMMAND(f, "ff", (float *a, float *b), intret((int)(*a > *b))); ICOMMAND(<=f, "ff", (float *a, float *b), intret((int)(*a <= *b))); ICOMMAND(>=f, "ff", (float *a, float *b), intret((int)(*a >= *b))); ICOMMAND(^, "ii", (int *a, int *b), intret(*a ^ *b)); ICOMMAND(!, "i", (int *a), intret(*a == 0)); ICOMMAND(&, "ii", (int *a, int *b), intret(*a & *b)); ICOMMAND(|, "ii", (int *a, int *b), intret(*a | *b)); ICOMMAND(~, "i", (int *a), intret(~*a)); ICOMMAND(^~, "ii", (int *a, int *b), intret(*a ^ ~*b)); ICOMMAND(&~, "ii", (int *a, int *b), intret(*a & ~*b)); ICOMMAND(|~, "ii", (int *a, int *b), intret(*a | ~*b)); ICOMMAND(<<, "ii", (int *a, int *b), intret(*a << *b)); ICOMMAND(>>, "ii", (int *a, int *b), intret(*a >> *b)); ICOMMAND(&&, "V", (char **args, int *numargs), ICOMMAND(||, "V", (char **args, int *numargs), ICOMMAND(div, "ii", (int *a, int *b), intret(*b ? *a / *b : 0)); ICOMMAND(mod, "ii", (int *a, int *b), intret(*b ? *a % *b : 0)); ICOMMAND(divf, "ff", (float *a, float *b), floatret(*b ? *a / *b : 0)); ICOMMAND(modf, "ff", (float *a, float *b), floatret(*b ? fmod(*a, *b) : 0)); ICOMMAND(sin, "f", (float *a), floatret(sin(*a*RAD))); ICOMMAND(cos, "f", (float *a), floatret(cos(*a*RAD))); ICOMMAND(tan, "f", (float *a), floatret(tan(*a*RAD))); ICOMMAND(asin, "f", (float *a), floatret(asin(*a)/RAD)); ICOMMAND(acos, "f", (float *a), floatret(acos(*a)/RAD)); ICOMMAND(atan, "f", (float *a), floatret(atan(*a)/RAD)); ICOMMAND(sqrt, "f", (float *a), floatret(sqrt(*a))); ICOMMAND(pow, "ff", (float *a, float *b), floatret(pow(*a, *b))); ICOMMAND(loge, "f", (float *a), floatret(log(*a))); ICOMMAND(log2, "f", (float *a), floatret(log(*a)/M_LN2)); ICOMMAND(log10, "f", (float *a), floatret(log10(*a))); ICOMMAND(exp, "f", (float *a), floatret(exp(*a))); ICOMMAND(min, "V", (char **args, int *numargs), ICOMMAND(max, "V", (char **args, int *numargs), ICOMMAND(minf, "V", (char **args, int *numargs), ICOMMAND(maxf, "V", (char **args, int *numargs), ICOMMAND(cond, "V", (char **args, int *numargs), CASECOMMAND(case, "i", int, parseint(args[i]) == *val); CASECOMMAND(casef, "f", float, parsefloat(args[i]) == *val); CASECOMMAND(cases, "s", char, !strcmp(args[i], val)); ICOMMAND(rnd, "ii", (int *a, int *b), intret(*a - *b > 0 ? rnd(*a - *b) + *b : *b)); ICOMMAND(strcmp, "ss", (char *a, char *b), intret(strcmp(a,b)==0)); ICOMMAND(=s, "ss", (char *a, char *b), intret(strcmp(a,b)==0)); ICOMMAND(!=s, "ss", (char *a, char *b), intret(strcmp(a,b)!=0)); ICOMMAND(s, "ss", (char *a, char *b), intret(strcmp(a,b)>0)); ICOMMAND(<=s, "ss", (char *a, char *b), intret(strcmp(a,b)<=0)); ICOMMAND(>=s, "ss", (char *a, char *b), intret(strcmp(a,b)>=0)); ICOMMAND(echo, "C", (char *s), conoutf("\f1%s", s)); ICOMMAND(error, "C", (char *s), conoutf(CON_ERROR, s)); ICOMMAND(strstr, "ss", (char *a, char *b), { char *s = strstr(a, b); intret(s ? s-a : -1); }); ICOMMAND(strlen, "s", (char *s), intret(strlen(s))); ICOMMAND(strreplace, "sss", (char *s, char *o, char *n), commandret = strreplace(s, o, n)); ICOMMAND(getmillis, "i", (int *total), intret(*total ? totalmillis : lastmillis)); COMMANDN(sleep, addsleep, "is"); COMMANDN(clearsleep, clearsleep_, "i"); IN THE "SRC/ENGINE/TEXTEDIT.H" (HEADERS) : #define PASTEBUFFER "#pastebuffer" (= showgui pastebuffer ; the hidden buffer gui !!!) TEXTCOMMAND(textcopy, "", (), editor *b = useeditor(PASTEBUFFER, EDITORFOREVER, false); top->copyselectionto(b);); TEXTCOMMAND(textpaste, "", (), editor *b = useeditor(PASTEBUFFER, EDITORFOREVER, false); top->insertallfrom(b);); TEXTCOMMAND(textmark, "i", (int *m), // (1=mark, 2=unmark), return current mark setting if no args if(*m) top->mark(*m==1); else result(top->region() ? "1" : "2"); ); TEXTCOMMAND(textselectall, "", (), top->selectall();); TEXTCOMMAND(textclear, "", (), top->clear();); TEXTCOMMAND(textcurrentline, "", (), result(top->currentline().text);); TEXTCOMMAND(textexec, "i", (int *selected), // execute script commands from the buffer (0=all, 1=selected region only) TEXTCOMMAND(textinit, "sss", (char *name, char *file, char *initval), // loads into named editor if no file assigned and editor has been rendered TEXTCOMMAND(textload, "s", (char *file), // loads into the topmost editor, returns filename if no args TEXTCOMMAND(textsave, "s", (char *file), // saves the topmost (filename is optional) TEXTCOMMAND(textprev, "", (), editors.insert(0, top); editors.pop();); // return to the previous editor TEXTCOMMAND(textmode, "i", (int *m), // (1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes)) topmost editor, return current setting if no args ICOMMAND(textfocus, "si", (char *name, int *mode), // focus on a (or create a persistent) specific editor, else returns current name TEXTCOMMAND(textshow, "", (), // @DEBUG return the start of the buffer ICOMMAND(textlist, "", (), // @DEBUG return list of all the editors